package pl.rzarajczyk.breaktime.utils;



import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author rzarajczyk
 */
public class Scheduler  {
    
    public static final int TIME_UNIT = Utils.SECOND;

    private Timer timer;

    private List<SchedulerEventWrapper> events;
    private boolean paused;
    private TimerTask task;

    private Log log = LogFactory.getLog(getClass());

    public Scheduler() {
        this.timer = new Timer();
        this.events = new CopyOnWriteArrayList<SchedulerEventWrapper>();
        this.paused = false;
        this.task = new TimerTask() {
            @Override
            public void run() {
                tick();
            }
        };
    }

    public void open() {
        timer.scheduleAtFixedRate(task, 0, Utils.SECOND);
    }
    
    void tick() {
        if ( ! paused ) {
            for ( SchedulerEventWrapper event : events ) {
                event.getEvent().onTick();
                if ( event.getEvent().interval() > 0 ) {
                    int schedule = event.getSchedule();
                    schedule -= Utils.SECOND;

                    if ( schedule <= 0 ) {
                        if ( event.getEvent().duration() == 0 ) {
                            event.getEvent().onStart();
                            event.getEvent().onFinish();
                            event.setSchedule( event.getEvent().interval() );
                        } else {
                            if ( event.isInProgress() ) {
                                event.getEvent().onFinish();
                                event.setInProgress(false);
                                event.setSchedule( event.getEvent().interval() );
                            } else {
                                event.getEvent().onStart();
                                event.setInProgress(true);
                                event.setSchedule( event.getEvent().duration() );
                            }
                        }
                    } else {
                        event.setSchedule( schedule );
                    }
                }
            }
        }
    }

    public void close() {
        timer.cancel();
        task = null;
        events.clear();
    }
    
    public boolean isPaused() {
        return paused;
    }
    
    public void setPaused(boolean paused) {
        this.paused = paused;
    }

    public void schedule(SchedulerEvent event) {
        this.events.add(new SchedulerEventWrapper(event));
    }

    public void unschedule(SchedulerEvent event) {
        this.events.remove(new SchedulerEventWrapper(event));
    }
    
    private class SchedulerEventWrapper {
        private SchedulerEvent event;
        private boolean inProgress;
        private int schedule;

        public SchedulerEventWrapper(SchedulerEvent event) {
            if ( event == null ) {
                throw new IllegalArgumentException(new NullPointerException("SchedulerEvent cannot be null"));
            }
            this.event = event;
            this.inProgress = false;
            this.schedule = event.interval();
        }

        public SchedulerEvent getEvent() {
            return event;
        }

        public boolean isInProgress() {
            return inProgress;
        }

        public void setInProgress(boolean inProgress) {
            this.inProgress = inProgress;
        }

        public void setSchedule(int schedule) {
            this.schedule = schedule;
        }

        public int getSchedule() {
            return schedule;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final SchedulerEventWrapper other = (SchedulerEventWrapper) obj;
            if (this.event != other.event && (this.event == null || !this.event.equals(other.event))) {
                return false;
            }
            if (this.inProgress != other.inProgress) {
                return false;
            }
            if (this.schedule != other.schedule) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            return hash;
        }
        
        
    }
}
